package handler

import (
	"fmt"
	"gitee.com/simple-set/simple.io/src/event"
	"gitee.com/simple-set/simple.io/src/socket"
	"gitee.com/simple-set/simple.proxy/src/config"
	"strings"
	"time"
)

type Client struct {
	dnsService  *DnsService
	bootClient  *event.BootClient
	interceptor *Interceptor
}

func NewClient() *Client {
	if config.Config.DnsServer == "" {
		return &Client{bootClient: event.NewBootClient()}
	} else {
		return &Client{bootClient: event.NewBootClient(), dnsService: NewDnsService(config.Config.DnsServer)}
	}
}

func NewClientPlus(processList ...event.Process) *Client {
	client := NewClient()
	for _, process := range processList {
		client.register(process)
	}
	return client
}

func (c *Client) SetInterceptor(interceptor *Interceptor) {
	c.interceptor = interceptor
}

func (c *Client) isInterceptor(addr string) bool {
	if c.interceptor == nil {
		return false
	}
	if split := strings.Split(addr, ":"); len(split) == 1 {
		return c.interceptor.Intercept(addr)
	} else {
		return c.interceptor.Intercept(split[0])
	}
}

func (c *Client) register(process event.Process) {
	c.bootClient.Register(process)
}

func (c *Client) resolveDomain(targetAddr string) (string, error) {
	var addr string
	var port string
	if split := strings.Split(targetAddr, ":"); len(split) != 2 {
		return "", fmt.Errorf("need address and port, invalid target address: %s", targetAddr)
	} else {
		addr = strings.TrimSpace(split[0])
		port = strings.TrimSpace(split[1])
	}

	if c.dnsService == nil {
		return fmt.Sprintf("%s:%s", addr, port), nil
	}

	if ipAddr, err := c.dnsService.AutoQuery(addr); err != nil {
		return "", err
	} else {
		return fmt.Sprintf("%s:%s", ipAddr, port), nil
	}
}

func (c *Client) DialTimeoutTcp(addr string, timeout time.Duration) (event.StarterClient, error) {
	ipAddr, err := c.resolveDomain(addr)
	if err != nil {
		return nil, err
	}
	if c.isInterceptor(ipAddr) {
		return nil, fmt.Errorf("TCP proxy request intercepted %s", ipAddr)
	}
	return c.bootClient.DialTcpTimeout(ipAddr, timeout)
}

func (c *Client) DialTcp(addr string) (event.StarterClient, error) {
	return c.DialTimeoutTcp(addr, time.Second*30)
}

func (c *Client) DialUdp(addr string) (event.StarterClient, error) {
	if ipAddr, err := c.resolveDomain(addr); err != nil {
		return nil, err
	} else {
		if c.isInterceptor(addr) {
			return nil, fmt.Errorf("UDP proxy request intercepted %s", ipAddr)
		}
		return c.bootClient.DialUdp(ipAddr)
	}
}

func (c *Client) DialUdpSocket(addr string) (*socket.Socket, error) {
	if ipAddr, err := c.resolveDomain(addr); err != nil {
		return nil, err
	} else {
		if c.isInterceptor(addr) {
			return nil, fmt.Errorf("UDP proxy request intercepted %s", ipAddr)
		}
		return socket.NewUdpClient().Dial(ipAddr)
	}
}
